package de.dlrg.wms.controller.controllers;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import wms2010.interfaces.devices.IDevice;
import de.dlrg.wms.controller.controllers.interfaces.ICompetitionCallback;
import de.dlrg.wms.dao.CompetitionStatusDao;
import de.dlrg.wms.dao.SwimmerDao;
import de.dlrg.wms.dao.SwimmerTrackRunDao;
import de.dlrg.wms.interfaces.controller.IDeviceCompetitionCallback;
import de.dlrg.wms.interfaces.controller.StopperEvent;
import de.dlrg.wms.model.CompetitionStatus;
import de.dlrg.wms.model.Run;
import de.dlrg.wms.model.SwimmerTrackRun;
import de.dlrg.wms.model.Track;

import static de.dlrg.wms.model.CompetitionStatus.RunState;

@Component
public class CompetitionController implements IDeviceCompetitionCallback {
	private List<ICompetitionCallback> callbacks = new ArrayList<ICompetitionCallback>();
	@Autowired
	private SwimmerTrackRunDao swimmerTrackRunDao;

	@Autowired
	private TrackController trackController;
	@Autowired
	private CompetitionStatusDao competitionStatusDao;
	@Autowired
	private RunController runController;

	private CompetitionStatus competitionStatus;

	public CompetitionStatus getCompetitionStatus() {
		return this.competitionStatus;
	}

	private void takeNextRun() {
		List<Run> runs = this.runController.readAll();
		int indexOfCurrentRun = runs.indexOf(competitionStatus.getCurrentRun());
		competitionStatus.setCurrentRun(runs.get(indexOfCurrentRun + 1));
		update();
	}

	private void update() {
		this.competitionStatusDao.save(this.competitionStatus);
		this.notifyListenersAboutUpdate();
	}

	private void notifyListenersAboutUpdate() {
		for (ICompetitionCallback callback : this.callbacks) {
			callback.updated(this.competitionStatus);
		}
	}

	public void registerUpdateListener(ICompetitionCallback callback) {
		this.callbacks.add(callback);
	}

	public void removeUpdateListener(ICompetitionCallback callback) {
		this.callbacks.remove(callback);
	}

	public void setFirstRun(Run run) {
		this.competitionStatus.setCurrentRun(run);
		this.competitionStatus.NOT_STARTED();
		update();
	}

	@PostConstruct
	public void postConstruct() {
		if (this.competitionStatusDao.count() <= 0) {
			this.competitionStatus = new CompetitionStatus();
			update();
			return;
		}
		try {
			this.competitionStatus = this.competitionStatusDao.readAll().get(0);
		} catch (Exception ex) {
			// okay database is not sane
			this.competitionStatusDao.deleteAll();
			this.postConstruct();
			ex.printStackTrace();
		}
		this.startStopCompetition(true);
	}

	public void startStopCompetition(boolean startStopCompetition) {
		this.competitionStatus.setPaused(!startStopCompetition);
		this.updateMaschine();
	}

	private void updateMaschine() {
		this.stopEvent(new StopperEvent().setDevice(new IDevice() {
			@Override
			public boolean isStarter() {
				return false;
			}

			@Override
			public void rumble() {
				// TODO Auto-generated method stub

			}
		}).setIgnoreEvent(true));
	}
	
	public void resetTime(SwimmerTrackRun swimmerTrackRun) {
		swimmerTrackRun.setTime(-1L);
		this.swimmerTrackRunDao.save(swimmerTrackRun);
		update();
	}
	
	public void stopEvent(StopperEvent stopperEvent) {
		switch (this.competitionStatus.getRunState()) {
		case NO_RUNS_AVAILABLE:
			break;
		case NOT_STARTED:
			if (!this.competitionStatus.isPaused()) {
				this.competitionStatus.WAITING_FOR_READY();
				this.competitionStatus.getNextWaitingSwimmerTrackRun();
				this.updateMaschine();
			}
			break;
		case WAITING_FOR_READY: {
			if (stopperEvent.isIgnoreEvent()) {
				break;
			}
			if (!stopperEvent.getDevice().isStarter()) {
				Track track = trackController.getTrackByDevice(stopperEvent
						.getDevice());
				SwimmerTrackRun swimmerTrackRun = this.competitionStatus
						.getCurrentRun().getSwimmerByTrack(track);
				if (swimmerTrackRun.equals(competitionStatus
						.getWaitingSwimmerTrackRun())) {
					swimmerTrackRun.setReady(true);
					this.competitionStatus.getNextWaitingSwimmerTrackRun();
					if (this.competitionStatus.getCurrentRun()
							.checkAllSwimmerReady()) {
						this.competitionStatus.WAITING_FOR_START();
					}
				}
			}
			break;
		}
		case WAITING_FOR_START:
			if (stopperEvent.isStarter()) {
				this.competitionStatus.getCurrentRun().setStartTime(
						stopperEvent.getTimeOfEvent());
				this.runController.createOrUpdate(this.getCompetitionStatus()
						.getCurrentRun());
				this.competitionStatus.RUNNING();
			}
			break;
		case RUNNING: {
			if (stopperEvent.isStarter()){
				this.competitionStatus.WAITING_FOR_START();
				this.updateMaschine();
			} else {
				Track track = trackController.getTrackByDevice(stopperEvent
						.getDevice());
				SwimmerTrackRun meantSwimmerTrackRun = this.competitionStatus
						.getCurrentRun().getSwimmerByTrack(track);
				// set time for swimmer
				if (!meantSwimmerTrackRun.isStopped()) {
					this.swimmerTrackRunDao.save(meantSwimmerTrackRun
							.setTime(Math.abs(stopperEvent.getTimeOfEvent()
									- this.competitionStatus.getCurrentRun()
											.getStartTime())));

					if (this.competitionStatus.getCurrentRun()
							.checkAllSwimmerStopped()) {
						this.competitionStatus.FINISHED();
						this.updateMaschine();
					}
				}
			}

			break;
		}
		case FINISHED:
			this.takeNextRun();
			this.competitionStatus.NOT_STARTED();
			this.updateMaschine();
			break;
		}

		update();
	}

}
